package com.lenovo.nova.util.fragment;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import android.app.DialogFragment;
import com.lenovo.nova.util.debug.mylog;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;
import com.lenovo.nova.widget.dialog.BaseDialogFragment;

@SuppressWarnings("rawtypes")
public class SystemFragment implements FragmentOperator {
	private static final String TAG = SystemFragment.class.getSimpleName();

	private static SystemFragment instance;

	private Map<String,FragmentWrapper> mMaps = new HashMap<String,FragmentWrapper>();

	private Activity activity;

	private BaseFragment mCurrentFragment;
	private BaseFragment mLastFragment;

    public SystemFragment() {
	}

	public static FragmentOperator getInstance() {
		if (instance == null) {
			instance = new SystemFragment();
		}
		return instance;
	}


    public void switchFragmentByHideShow(Class<? extends BaseFragment> current, Class<? extends BaseFragment> to, Bundle bundle) {
        CreateFragmentWrapperHelp help = new CreateFragmentWrapperHelp() {
            @Override
            public FragmentWrapper createWrapper(Activity activity, String tag, Class<? extends BaseFragment> clz, Bundle args) {
                return new SwitchFragWrapperForHide(activity,tag,clz,args);
            }
        };

        switchFragmentWrapper(current, to, bundle, -1, -1, help);
    }

	@Override
	public void switchFragmentByAttached(Class<? extends BaseFragment> currentFragmentClz,
										 Class<? extends BaseFragment> toFragmentClz, Bundle bundle,
										 int animationEnter,
										 int animationExit

	) {
        CreateFragmentWrapperHelp help = new CreateFragmentWrapperHelp() {
            @Override
            public FragmentWrapper createWrapper(Activity activity, String tag, Class<? extends BaseFragment> clz, Bundle args) {
                return new SwitchFragWrapperForAttach(activity,tag,clz,args);
            }
        };

        switchFragmentWrapper(currentFragmentClz, toFragmentClz, bundle, animationEnter, animationExit, help);
	}

    @Override
    public void switchByHideAndAddWithStack(Fragment current,
                                            Class<? extends BaseFragment> to,
                                            Bundle bundle) {
        FragmentTransaction tr = activity.getFragmentManager().beginTransaction();

        Fragment oldFragment = activity.getFragmentManager().findFragmentByTag(to.getName());
        mylog.i(TAG,"oldFragment " + oldFragment);

        Fragment toFragment = Fragment.instantiate(activity, to.getName(), bundle);
        tr.hide(current);
        tr.add(android.R.id.content,toFragment,to.getName());
        tr.addToBackStack(null);
        try {
            tr.commit();
        } catch (Exception e) {
            tr.commitAllowingStateLoss();
            e.printStackTrace();
        }
    }

    @Override
	public void switchFragmentByAttached(Class<? extends BaseFragment> current, Class<? extends BaseFragment> to, Bundle bundle) {
        switchFragmentByAttached(current, to, bundle, 0, 0);
	}

	@Override
	public void switchFragmentByAttached(Class<? extends BaseFragment> currentFragmentClz, Class<? extends BaseFragment> toFragmentClz) {
		switchFragmentByAttached(currentFragmentClz, toFragmentClz, null);
	}

    @Override
    public void replaceFragment(Class<? extends BaseFragment> current, Class<? extends BaseFragment> target, boolean add, Bundle bundle) {
        replaceFragment(current, target, add, bundle, 0, 0, 0, 0);
    }

    @Override
    public void replaceFragment(Class<? extends BaseFragment> current,
                                Class<? extends BaseFragment> target,
                                boolean add,
                                Bundle bundle,
                                int enter,
                                int exit,
                                int popEnter,
                                int popExit) {

        if(current != null){
            mMaps.remove(current.getName());
        }

        FragmentTransaction transaction = activity.getFragmentManager().beginTransaction();

        if(enter >0 && exit > 0){

        }
        transaction.setCustomAnimations(enter,exit,popEnter,popExit);
        BaseFragment fragment = (BaseFragment) Fragment.instantiate(activity,target.getName(),bundle);


        transaction.replace(android.R.id.content,fragment,target.getName());
        if(add){
            transaction.addToBackStack(null);
        }
        transaction.commit();

        mLastFragment = null;
        mCurrentFragment = fragment;
    }

	@Override
	public void pop() {
		activity.getFragmentManager().popBackStack();
	}

	@Override
	public void init(Activity activity, Object... objects) {
		this.activity = activity;
	}

	@Override
	public void release() {
		this.activity = null;
		Iterator<Entry<String, FragmentWrapper>> it = mMaps.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, FragmentWrapper> value = it.next();
			value.getValue().release();
		}
		
		mMaps.clear();
		
	}

    @Override
    public ArrayList<Fragment> getFragmentList() {
        try {
            Field mAddMethod = activity.getFragmentManager().getClass().getDeclaredField("mAdded");
            mAddMethod.setAccessible(true);
            ArrayList<Fragment> mAdd = (ArrayList<Fragment>) mAddMethod.get(activity.getFragmentManager());
            return mAdd;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Fragment getCurrentFragmentFilterDialogFragment() {
        ArrayList<Fragment> list = getFragmentList();
        for (int i = list.size() - 1; i >= 0; i--) {
            Fragment fragment = list.get(i);
            if(fragment instanceof DialogFragment){
                continue;
            }

            if(fragment instanceof BaseFragment){
                return fragment;
            }

        }
        return null;
    }

    @Override
	public BaseFragment getLastFragment() {
		return mLastFragment;
	}

	@Override
	public boolean executeBackup() {
		if(mCurrentFragment != null && mLastFragment != null){
            if(mCurrentFragment== mLastFragment){
                return false;
            }

			switchFragmentByAttached(mCurrentFragment.getClass(), mLastFragment.getClass());
		    return true;
        }
        return false;
	}
	
	@Override
	public BaseFragment getCurrentFragment() {
		return mCurrentFragment;
	}

    @Override
    public void setCurrentFragment(BaseFragment fragment) {
        mCurrentFragment = fragment;
    }


    private void switchFragmentWrapper(Class<? extends BaseFragment> currentFragmentClass,
                                       Class<? extends BaseFragment> toFragmentClass, Bundle bundle,
                                       int animationEnter,
                                       int animationExit,
                                       CreateFragmentWrapperHelp help

    ) {
        String currentFrgName = null;
        String toFrgName = null;

        if(currentFragmentClass != null){
            currentFrgName = currentFragmentClass.getName();
        }
        if(toFragmentClass != null){
            toFrgName = toFragmentClass.getName();
        }

        mylog.i(TAG, "currentFrgName " + currentFrgName + "  toFrgName " + toFrgName);

        FragmentTransaction tr = null;
        try {
            tr = activity.getFragmentManager().beginTransaction().disallowAddToBackStack();
            if(animationEnter > 0 && animationExit > 0){
                tr.setCustomAnimations(animationEnter,animationExit);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        if(currentFrgName != null && toFrgName != null && currentFrgName.equals(toFrgName)){
            FragmentWrapper fragmentWrapper = createFragmentWrapperByFragName(currentFragmentClass, bundle, help);
            fragmentWrapper.onTabReselected(tr);
            return ;
        }

        if(currentFrgName != null){
            FragmentWrapper fragmentWrapper = createFragmentWrapperByFragName(currentFragmentClass, bundle, help);
            fragmentWrapper.onTabUnselected(tr);
            mLastFragment =  (BaseFragment) fragmentWrapper.getFragment();
        }

        if(toFrgName != null){
            FragmentWrapper fragmentWrapper = createFragmentWrapperByFragName(toFragmentClass, bundle, help);
            fragmentWrapper.onTabSelected(tr, bundle);
            mCurrentFragment = (BaseFragment) fragmentWrapper.getFragment();
        }

        if (!tr.isEmpty()) {
            try {
                tr.commit();
            } catch (Exception e) {
                tr.commitAllowingStateLoss();
                e.printStackTrace();
            }
        }
    }

    private FragmentWrapper createFragmentWrapperByFragName(Class<? extends BaseFragment>
                                                                    fragmentClass,
                                                            Bundle bundle,
                                                            CreateFragmentWrapperHelp help
                                                            ) {
        if(fragmentClass == null){
            return null;
        }

        FragmentWrapper currentWrapper;
        String currentFrgName = fragmentClass.getName();
        if(mMaps.containsKey(currentFrgName)){
            currentWrapper = mMaps.get(currentFrgName);
        }else{
            currentWrapper = help.createWrapper(activity, currentFrgName, fragmentClass, bundle);
            mMaps.put(currentFrgName, currentWrapper);
        }
        return currentWrapper;
    }

    public static class SwitchFragWrapperForHide implements FragmentWrapper{
        private Activity mActivity;
        private String mTag;
        private Class<? extends BaseFragment> mClass;
        private Bundle mArgs;
        private Fragment mFragment;

        public SwitchFragWrapperForHide(Activity activity,
                                          String tag,
                                          Class<? extends BaseFragment> clz,
                                          Bundle args) {

            mActivity = activity;
            mTag = tag;
            mClass = clz;
            mArgs = args;

            // Check to see if we already have a fragment for this tab, probably
            // from a previously saved state.  If so, deactivate it, because our
            // initial state is that a tab isn't shown.
            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment != null && !mFragment.isDetached()) {
                FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
                ft.hide(mFragment);
                ft.commit();
            }
        }

        @Override
        public void onTabSelected(FragmentTransaction ft, Bundle args) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {
                if(args != null && args != mArgs){
                    mArgs = args;
                    mFragment.getArguments().clear();
                    mFragment.getArguments().putAll(mArgs);
                }
                ft.show(mFragment);
            }
        }

        @Override
        public void onTabUnselected(FragmentTransaction ft) {
            if (mFragment != null) {
                ft.hide(mFragment);
            }
        }

        public void onTabReselected(FragmentTransaction ft) {
            Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public Fragment getFragment() {
            return mFragment;
        }

        @Override
        public void release() {

        }
    }

	public static class SwitchFragWrapperForAttach implements FragmentWrapper {
        private Activity mActivity;
        private String mTag;
        private Class<? extends BaseFragment> mClass;
        private Bundle mArgs;
        private Fragment mFragment;

		public void release() {
        	mActivity = null;
        	mClass = null;
        	mArgs = null;
        	mFragment = null;
		}

		public SwitchFragWrapperForAttach(Activity activity,
                                          String tag,
                                          Class<? extends BaseFragment> clz,
                                          Bundle args) {

            mActivity = activity;
            mTag = tag;
            mClass = clz;
            mArgs = args;

            // Check to see if we already have a fragment for this tab, probably
            // from a previously saved state.  If so, deactivate it, because our
            // initial state is that a tab isn't shown.
            mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
            if (mFragment != null && !mFragment.isDetached()) {
                FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
                ft.detach(mFragment);
                ft.commit();
            }
        }

		
        public void onTabSelected(FragmentTransaction ft, Bundle args) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {
            	if(args != null && args != mArgs){
            		mArgs = args;
            		mFragment.getArguments().clear();
            		mFragment.getArguments().putAll(mArgs);
            	}
                ft.attach(mFragment);
            }
        }

        public void onTabUnselected(FragmentTransaction ft) {
            if (mFragment != null) {
                ft.detach(mFragment);
            }
        }

        public void onTabReselected(FragmentTransaction ft) {
            Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public Fragment getFragment() {
            return mFragment;
        }
    }


    public interface CreateFragmentWrapperHelp{
        FragmentWrapper createWrapper(Activity activity,
                                      String tag,
                                      Class<? extends BaseFragment> clz,
                                      Bundle args);
    }

    public interface FragmentWrapper {
        public void onTabSelected(FragmentTransaction ft, Bundle args);
        public void onTabUnselected(FragmentTransaction ft);
        public void onTabReselected(FragmentTransaction ft);
        public Fragment getFragment();
        public void release();
    }

}